Repository navigation
feat(worker): run the operator console as a service (#708) - #728
Merged
Merged
Conversation
Part of #708. `pi-dispatch service install --console` keeps the web console running across reboots, so nobody has to keep a terminal open for `pi-dispatch-console serve`. - The console is a third service component beside the worker and --receiver (not together with --receiver): a systemd user or staged system unit, a launchd agent (com.pi-dispatch.console), or an nssm service. It runs `node <console.mjs> serve --deployment <folder>`. - The console is found through the admin package beside the worker. A `pi-dispatch-console` on PATH counts only if it resolves to the package's own dist/console.mjs, so shims are refused. Install refuses when it is missing, or when the tokens file has no operator token, since serve would then exit 2 on every start. - The console never loads the whole .env: console.service has no EnvironmentFile=, and its plist and nssm entry set PI_WRAPPER_NO_DOTENV=1, which both wrappers check before the load. serve --deployment reads its own keys and still refuses an untrusted .env. - ExecStart words are quoted the systemd way, so a folder holding a space, %, $, quotes or ; renders and reads back as itself. The plist escapes & < >, and --env-setup moves a non-plain argv after the script as separate words. Ordinary worker and receiver renders are byte-identical to before. - New worker/src/console-probe.mjs (`./console-probe`) is the one healthz probe rule. It contacts only loopback or the console's socket, never follows a redirect, and gives up after 1 s. - Doctor reports an installed console unit and whether the console answers. It says nothing for a deployment with no console configured. - `up` suggests the install when an operator token exists and no unit does. --drain is refused for anything but the worker. - deploy/console.service is new (mirrored in worker/deploy). deploy-lint verifies it and a rendered special-path unit. Specs: DES-OPERATOR-CONSOLE (the console as a service), DES-SERVICE-ENV-SETUP-SEAM, INT-CONSOLE-TOKENS-FILE-CONTRACT and REQ-DEPLOYMENT-BOOTSTRAP are amended. Signed-off-by: Rob Boerman <robboerman@live.nl>
edgehero
added a commit
that referenced
this pull request
Oct 21, 2026
…console (#708) Part of #708. An operator in pi types `/dispatch console` and gets the console's address, whether it is running and, with --link, a one-time sign-in link, all in one step. - `/dispatch console [page] [--link [name]] [--no-open]` is operator-typed only. Its output goes to the operator's notice, never to model context, and no tool exposes it. `page` is runs, fleet, insights, config, parked, audit or a run id. The address follows consolePageLines' rules. - Running or not: the worker's console probe (#728, the one rule doctor uses) asks /healthz on loopback or the socket only. "Not running" names `pi-dispatch-console serve` and `service install --console`. - --link mints a 60 s, single-use login code in-process for an operator token (asking which when there are several). It appends a `panel` audit row first, and pairs it with a `failed` row under the same ticket if the mint is refused. It mints only when the console answers as running, only in pi's TUI mode, and only where the codes directory is guarded. The link can carry a return page (`#<code>&next=/runs`), judged by the server and by the page script against the console's own pages. - On a local session (no SSH, a display, a TCP listener) the link is handed to the browser through a 0600 page in a 0700 temporary directory, so the code never appears in a process's arguments. The URL is always printed, and --no-open only prints. On Windows the opener uses url.dll so a URL holding & survives. - The login-codes directory joins the guarded files, as the tokens file is, judged by identity even before it exists. The console also refuses a code file that another account can read or owns, and one whose expiry exceeds the code's lifetime. Specs: REQ-ADMIN-VIA-PI-EXTENSION and DES-ADMIN-VIA-PI-EXTENSION (the subcommand; a browser only for it on a local session), OQ-024 (reopened and closed with the local-session rule), DES-OPERATOR-CONSOLE (the panel as a second minter, the return-path rule, threat 4 covering the login codes) and INT-CONSOLE-AUDIT-LOG are amended. UNCHANGED, checked: "binds no network port", the model-callable tool list and REQ-CONSOLE-SECURITY. Signed-off-by: Rob Boerman <robboerman@live.nl>
This was referenced Oct 21, 2026
Closed
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #708.
pi-dispatch service install --consolekeeps the web console running across reboots.What changes
--receiver(the two flags cannot be combined). It installs as a systemd user unit or a staged system unit, a launchd agent (com.pi-dispatch.console), or an nssm service, and runsnode <console.mjs> serve --deployment <folder>.pi-dispatch-consoleon PATH counts only if it resolves to the package's owndist/console.mjs, so pnpm and Volta shims are refused.servewould exit 2 on every start)..env.console.servicehas noEnvironmentFile=, and the plist and nssm entry setPI_WRAPPER_NO_DOTENV=1, which both wrappers check before loading.env.serve --deploymentreads only its own keys, and still refuses an untrusted.env.ExecStartwords are quoted the systemd way, and the plist escapes& < >. A folder holding a space,%,$, quotes or;renders and reads back as itself. Ordinary worker and receiver renders are byte-identical to before.worker/src/console-probe.mjs, which the admin's/dispatch consolewill import. It contacts only loopback or the console's socket.upsuggests the install when an operator token exists and no unit does.--drainis refused for anything but the worker.Specs
Amended:
DES-OPERATOR-CONSOLE,DES-SERVICE-ENV-SETUP-SEAM,INT-CONSOLE-TOKENS-FILE-CONTRACT,REQ-DEPLOYMENT-BOOTSTRAP.Residuals
serveexit 2 on every start, and the unit stays stopped. The fix line names the lock path." % & | < > ^in any command word.Verification
up, env-file and publish all pass, and a test-side systemd unquoter checks that the decoded argv matches the intended one.systemd-analyze verify), which isn't installed locally.